package cn.juque.datapipeline.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.juque.common.base.PageInfo;
import cn.juque.common.constants.MessageEnum;
import cn.juque.common.exception.AppException;
import cn.juque.datapipeline.api.enums.DataPipelineMsgEnum;
import cn.juque.datapipeline.api.enums.TaskStatusEnum;
import cn.juque.datapipeline.api.enums.TaskTypeEnum;
import cn.juque.datapipeline.bo.task.FieldAddBO;
import cn.juque.datapipeline.bo.task.FieldUpdateBO;
import cn.juque.datapipeline.bo.task.TaskAddBO;
import cn.juque.datapipeline.bo.task.TaskUpdateBO;
import cn.juque.datapipeline.domain.TaskInfoDO;
import cn.juque.datapipeline.domain.TaskInfoSearchParamDO;
import cn.juque.datapipeline.entity.DatabaseInfo;
import cn.juque.datapipeline.entity.TaskFieldMappingInfo;
import cn.juque.datapipeline.entity.TaskInfo;
import cn.juque.datapipeline.entity.TaskScript;
import cn.juque.datapipeline.mapper.DatabaseInfoMapper;
import cn.juque.datapipeline.mapper.TaskFieldMappingInfoMapper;
import cn.juque.datapipeline.mapper.TaskInfoMapper;
import cn.juque.datapipeline.mapper.TaskScriptMapper;
import cn.juque.datapipeline.service.ITaskInfoService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author JUQUE
 * @version 1.0.0
 * <ul>
 *  <li>simple-generator</li>
 *  <li>任务信息</li>
 * </ul>
 * {@code @date} 2023-03-31 11:52:16
 **/
@Service("taskInfoService")
public class TaskInfoServiceImpl implements ITaskInfoService {

    @Resource
    private TaskInfoMapper taskInfoMapper;

    @Resource
    private DatabaseInfoMapper databaseInfoMapper;

    @Resource
    private TaskScriptMapper taskScriptMapper;

    @Resource
    private TaskFieldMappingInfoMapper taskFieldMappingInfoMapper;

    /**
     * 分页查询
     *
     * @param pageInfo pageInfo
     * @param taskInfo taskInfo
     * @return DataGrid
     */
    @Override
    public List<TaskInfoDO> listInfoWithPage(PageInfo pageInfo, TaskInfoSearchParamDO taskInfo) {
        Page<TaskInfoDO> page = new Page<>(pageInfo.getPage(), pageInfo.getLimit());
        List<TaskInfoDO> list = this.taskInfoMapper.queryInfoList(page, taskInfo);
        this.build(list);
        pageInfo.setTotal(page.getTotal());
        return list;
    }

    /**
     * 查询
     *
     * @param taskInfo taskInfo
     * @return List
     */
    @Override
    public List<TaskInfoDO> listInfoWithoutPage(TaskInfoSearchParamDO taskInfo) {
        List<TaskInfoDO> list = this.taskInfoMapper.queryInfoList(taskInfo);
        this.build(list);
        return list;
    }

    /**
     * 详情
     *
     * @param id 主键
     * @return TaskInfo
     */
    @Override
    public TaskInfoDO detail(String id) {
        TaskInfo taskInfo = this.taskInfoMapper.selectById(id);
        TaskInfoDO taskInfoDO = new TaskInfoDO();
        BeanUtil.copyProperties(taskInfo, taskInfoDO);
        this.build(CollUtil.newArrayList(taskInfoDO));
        this.buildMapping(taskInfoDO);
        return taskInfoDO;
    }

    /**
     * 保存
     *
     * @param addBO TaskAddBO
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveInfo(TaskAddBO addBO) {
        TaskInfo taskInfo = new TaskInfo();
        BeanUtil.copyProperties(addBO, taskInfo);
        taskInfo.setTaskStatus(TaskStatusEnum.ENABLE.getCode());
        boolean flag = SqlHelper.retBool(this.taskInfoMapper.insert(taskInfo));
        if (!flag) {
            throw new AppException(MessageEnum.SAVE_FAIL);
        }
        if (TaskTypeEnum.CHECK.getCode().equals(taskInfo.getTaskType())) {
            return;
        }
        List<FieldAddBO> fieldList = addBO.getFieldList();
        if (TaskTypeEnum.INSERT.getCode().equals(taskInfo.getTaskType()) && CollUtil.isEmpty(fieldList)) {
            throw new AppException(DataPipelineMsgEnum.FIELD_MAPPING_MISSING);
        }
        fieldList.forEach(f -> {
            TaskFieldMappingInfo mappingInfo = new TaskFieldMappingInfo();
            BeanUtil.copyProperties(f, mappingInfo);
            mappingInfo.setTaskId(taskInfo.getId());
            this.taskFieldMappingInfoMapper.insert(mappingInfo);
        });
    }

    /**
     * 更新
     *
     * @param updateBO TaskUpdateBO
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateInfo(TaskUpdateBO updateBO) {
        TaskInfo taskInfo = new TaskInfo();
        BeanUtil.copyProperties(updateBO, taskInfo);
        boolean flag = SqlHelper.retBool(this.taskInfoMapper.updateById(taskInfo));
        if (!flag) {
            throw new AppException(MessageEnum.UPDATE_FAIL);
        }
        List<FieldUpdateBO> fieldList = updateBO.getFieldList();
        if (TaskTypeEnum.INSERT.getCode().equals(taskInfo.getTaskType()) && CollUtil.isEmpty(fieldList)) {
            throw new AppException(DataPipelineMsgEnum.FIELD_MAPPING_MISSING);
        }
        TaskFieldMappingInfo mappingQuery = new TaskFieldMappingInfo();
        mappingQuery.setTaskId(taskInfo.getId());
        this.taskFieldMappingInfoMapper.delete(new QueryWrapper<>(mappingQuery));
        fieldList.forEach(f -> {
            TaskFieldMappingInfo mappingInfo = new TaskFieldMappingInfo();
            BeanUtil.copyProperties(f, mappingInfo);
            mappingInfo.setTaskId(taskInfo.getId());
            this.taskFieldMappingInfoMapper.insert(mappingInfo);
        });
    }

    /**
     * 切换状态
     *
     * @param taskId 任务ID
     */
    @Override
    public void triggerEnable(String taskId) {
        TaskInfo taskInfo = this.taskInfoMapper.selectById(taskId);
        if (TaskStatusEnum.DISABLED.getCode().equals(taskInfo.getTaskStatus())) {
            taskInfo.setTaskStatus(TaskStatusEnum.ENABLE.getCode());
        } else {
            taskInfo.setTaskStatus(TaskStatusEnum.DISABLED.getCode());
        }
        this.taskInfoMapper.updateById(taskInfo);
    }

    /**
     * 删除
     *
     * @param ids 主键列表
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteInfo(List<String> ids) {
        if (CollUtil.isEmpty(ids)) {
            return;
        }
        int count = this.taskInfoMapper.deleteBatchIds(ids);
        if (count != ids.size()) {
            throw new AppException(MessageEnum.DELETE_FAIL);
        }
        ids.forEach(f -> {
            TaskFieldMappingInfo mappingQuery = new TaskFieldMappingInfo();
            mappingQuery.setTaskId(f);
            this.taskFieldMappingInfoMapper.delete(new QueryWrapper<>(mappingQuery));
        });
    }

    private void build(List<TaskInfoDO> list) {
        if (CollUtil.isEmpty(list)) {
            return;
        }
        Set<String> sourceIdSet = list.stream().map(TaskInfo::getSourceDatabase).collect(Collectors.toSet());
        Set<String> targetIdSet = list.stream().map(TaskInfo::getTargetDatabase).collect(Collectors.toSet());
        sourceIdSet.addAll(targetIdSet);
        List<DatabaseInfo> databaseInfoList = this.databaseInfoMapper.selectBatchIds(sourceIdSet);
        Map<String, DatabaseInfo> databaseMap = databaseInfoList.stream().collect(Collectors.toMap(DatabaseInfo::getId, t -> t, (k1, k2) -> k2));
        Set<String> scriptIdSet = list.stream().map(TaskInfo::getScriptId).collect(Collectors.toSet());
        List<TaskScript> scripts = this.taskScriptMapper.selectBatchIds(scriptIdSet);
        Map<String, TaskScript> scriptMap = scripts.stream().collect(Collectors.toMap(TaskScript::getId, t -> t, (k1, k2) -> k2));
        list.forEach(f -> {
            DatabaseInfo databaseInfo = databaseMap.get(f.getSourceDatabase());
            f.setSourceDatabaseName(databaseInfo.getAliasName());
            databaseInfo = databaseMap.get(f.getTargetDatabase());
            f.setTargetDatabaseName(databaseInfo.getAliasName());
            TaskScript taskScript = scriptMap.get(f.getScriptId());
            f.setScriptName(taskScript.getScriptName());
        });
    }

    private void buildMapping(TaskInfoDO taskInfoDO) {
        TaskFieldMappingInfo query = new TaskFieldMappingInfo();
        query.setTaskId(taskInfoDO.getId());
        List<TaskFieldMappingInfo> list = this.taskFieldMappingInfoMapper.selectList(new QueryWrapper<>(query));
        List<TaskFieldMappingInfo> doList = BeanUtil.copyToList(list, TaskFieldMappingInfo.class);
        taskInfoDO.setFieldList(doList);
    }
}
